#pragma once
#include <boost/circular_buffer.hpp>
#include <numeric>


namespace Formula {
	class IVariable
	{
	public:
		typedef boost::circular_buffer<double> Values;
		typedef Values::const_reverse_iterator const_iterator;

		virtual bool CalcNewValue() const = 0;
		virtual void Release() const = 0;
		virtual double GetValue() const = 0;
		virtual size_t GetMax() const = 0;
		virtual int GetVersion() const = 0;

		virtual const_iterator begin() const = 0;

		virtual const_iterator end() const = 0;
		virtual size_t size() const = 0;
		virtual void AddValue(double value) const = 0;
		virtual void SetVersion(int version) const = 0;

	};
	class Variable : public IVariable
	{
	public:
		Variable(size_t max)
			:m_values(max),m_version(-1)
		{
		}

		double GetValue() const
		{
			return m_values.back();
		}

		size_t GetMax() const
		{
			return m_values.capacity();
		}

		int GetVersion() const
		{
			return m_version;
		}

		const_iterator begin() const
		{
			return m_values.rbegin();
		}

		const_iterator end() const
		{
			return m_values.rend();
		}
		size_t size() const
		{
			return m_values.size();
		}

		void AddValue(double value) const
		{
			m_values.push_back(value);
		}

		void SetVersion(int version) const		
		{
			m_version = version;
		}

	private:
		mutable Values m_values;
		mutable int m_version;
	};

	inline double ValueOf(double value)
	{
		return value;
	}

	template<typename T>
	inline double ValueOf(T& t)
	{
		return t.GetValue();
	}

	struct Plus
	{
		template<typename LT,typename RT>
		static double apply(LT const& left, RT const& right)
		{
			return ValueOf(left) + ValueOf(right);
		}
	};

	struct Minus
	{
		template<typename LT,typename RT>
		static double apply(LT const& left, RT const& right)
		{
			return ValueOf(left) - ValueOf(right);
		}
	};

	struct Time
	{
		template<typename LT,typename RT>
		static double apply(LT const& left, RT const& right)
		{
			return ValueOf(left) * ValueOf(right);
		}
	};

	struct Div
	{
		template<typename LT,typename RT>
		static double apply(LT const& left, RT const& right)
		{
			return ValueOf(left) / ValueOf(right);
		}
	};

	template<typename LT,class OP, typename RT>
	struct ET : public Variable
	{
		mutable LT m_left;
		mutable RT m_right;
	public:
		ET(LT const& left,RT const& right)
			:Variable(left.GetMax()),m_left(left),m_right(right)
		{
		}
		bool CalcNewValue() const
		{
			if (m_left.CalcNewValue() && m_right.CalcNewValue())
			{
				if (GetVersion() == -1 || 
					(GetVersion() < m_left.GetVersion() && m_left.GetVersion() == m_right.GetVersion()))
				{
					AddValue(OP::apply(m_left,m_right));
					SetVersion(m_left.GetVersion());
				}
				return true;
			}
			return false;
		}
		void Release() const
		{
			m_left.Release();
			m_right.Release();
		}
	};

	template<typename LT,class OP>
	struct ET<LT,OP,double> : public Variable
	{
		mutable LT m_left;
		double m_right;
	public:
		ET(LT const& left,double right)
			:Variable(left.GetMax()),m_left(left),m_right(right)
		{
		}
		bool CalcNewValue() const
		{
			if (!m_left.CalcNewValue())
			{
				return false;
			}
			if (GetVersion() == -1 || GetVersion() < m_left.GetVersion())
			{
				AddValue(OP::apply(m_left,m_right));
				SetVersion(m_left.GetVersion());
			}
			return true;
		}
		void Release() const
		{
			m_left.Release();
		}

	};

	template<typename RT,class OP>
	struct ET<double,OP,RT> : public Variable
	{
		double m_left;
		mutable RT m_right;
	public:
		ET(double left,RT const& right)
			:Variable(right.GetMax()),m_left(left),m_right(right)
		{
		}
		bool CalcNewValue() const
		{
			if (!m_right.CalcNewValue())
			{
				return false;
			}
			if (GetVersion() == -1 || GetVersion() < m_right.GetVersion())
			{
				AddValue(OP::apply(m_left,m_right));
				SetVersion(m_right.GetVersion());
			}
			return true;
		}
		void Release() const
		{
			m_right.Release();
		}
	};

	class VariableHolder : public IVariable
	{
	public:
		VariableHolder(IVariable* variable)
			:m_variable(variable)
		{
		}
		bool CalcNewValue() const
		{
			return m_variable->CalcNewValue();
		}
		double GetValue() const
		{
			return m_variable->GetValue();
		}
		size_t GetMax() const
		{
			return m_variable->GetMax();
		}

		int GetVersion() const
		{
			return m_variable->GetVersion();
		}

		Variable::const_iterator begin() const
		{
			return m_variable->begin();
		}

		Variable::const_iterator end() const
		{
			return m_variable->end();
		}
		size_t size() const
		{
			return m_variable->size();
		}
		void AddValue(double value) const
		{
			m_variable->AddValue(value);
		}
		void SetVersion(int version) const
		{
			m_variable->SetVersion(version);
		}
		void Release() const
		{
			if (m_variable)
			{
				m_variable->Release();
				delete m_variable;
				m_variable = 0;
			}
		}
	private:
		mutable IVariable* m_variable;
	};

	template<class LT,class RT>
	VariableHolder operator+(LT const& left, RT const& right)
	{
		return VariableHolder(new ET<LT,Plus,RT>(left,right));
	}

	template<class LT>
	VariableHolder operator+(LT const& left, double right)
	{
		return VariableHolder(new ET<LT,Plus,double>(left,right));
	}

	template<class RT>
	VariableHolder operator+(double left, RT const& right)
	{
		return VariableHolder(new ET<double,Plus,RT>(left,right));
	}


	template<class LT,class RT>
	VariableHolder operator-(LT const& left, RT const& right)
	{
		return VariableHolder(new ET<LT,Minus,RT>(left,right));
	}

	template<class LT>
	VariableHolder operator-(LT const& left, double right)
	{
		return VariableHolder(new ET<LT,Minus,double>(left,right));
	}

	template<class RT>
	VariableHolder operator-(double left, RT const& right)
	{
		return VariableHolder(new ET<double,Minus,RT>(left,right));
	}

	template<class LT,class RT>
	VariableHolder operator*(LT const& left, RT const& right)
	{
		return VariableHolder(new ET<LT,Time,RT>(left,right));
	}
	template<class LT>
	VariableHolder operator*(LT const& left, double right)
	{
		return VariableHolder(new ET<LT,Time,double>(left,right));
	}
	template<class RT>
	VariableHolder operator*(double left, RT const& right)
	{
		return VariableHolder(new ET<double,Time,RT>(left,right));
	}

	template<class LT,class RT>
	VariableHolder operator/(LT const& left, RT const& right)
	{
		return VariableHolder(new ET<LT,Div,RT>(left,right));
	}
	template<class LT>
	VariableHolder operator/(LT const& left, double right)
	{
		return VariableHolder(new ET<LT,Div,double>(left,right));
	}
	template<class RT>
	VariableHolder operator/(double left, RT const& right)
	{
		return VariableHolder(new ET<double,Div,RT>(left,right));
	}
}

class BasicVariable : public Formula::Variable
{
public:
	static Formula::VariableHolder Create(int max)
	{
		return Formula::VariableHolder(new BasicVariable(max));
	}
	bool CalcNewValue() const {return true;}
	void Release() const {}
	void AddValue(double value) const
	{
		Variable::AddValue(value);
		SetVersion(GetVersion()+1);
	}
private:
	BasicVariable(int max):Variable(max){}

};

class MAVariable : public Formula::Variable
{
public:
	MAVariable(Formula::VariableHolder const& parent,size_t count)
		:Variable(parent.GetMax()),m_parent(parent),m_count(count)
	{
	}
	bool CalcNewValue() const
	{
		if (!m_parent.CalcNewValue())
		{
			return false;
		}
		if (m_parent.size() >= m_count)
		{
			if (GetVersion() == -1 || GetVersion() < m_parent.GetVersion())
			{
				AddValue(std::accumulate(m_parent.begin(), m_parent.begin() + m_count,0.0)/m_count);
				SetVersion(m_parent.GetVersion());
			}
			return true;
		}
		return false;
	}
	void Release() const
	{
		m_parent.Release();
	}
private:
	mutable Formula::VariableHolder m_parent;
	size_t m_count;
};

class EMAVariable : public Formula::Variable
{
public:
	EMAVariable(Formula::VariableHolder const& parent,size_t count)
		:Variable(parent.GetMax()),m_parent(parent),m_count(count)
	{
	}
	bool CalcNewValue() const
	{
		if (!m_parent.CalcNewValue())
		{
			return false;
		}
		if (m_parent.size() >= m_count)
		{
			if (GetVersion() == -1 || GetVersion() < m_parent.GetVersion())
			{
				const_iterator iter = m_parent.begin();
				AddValue(innerEMA(iter,m_count));
				SetVersion(m_parent.GetVersion());
			}
			return true;
		}
		return false;
	}
	void Release() const
	{
		m_parent.Release();
	}
private:
	double innerEMA(const_iterator& iter, size_t N) const
	{
		if (N == 1)
		{
			return *iter;
		}
		else
		{
			return (*iter * 2 + (N-1)* innerEMA(++iter,N-1))/(N+1);
		}
	}

	mutable Formula::VariableHolder m_parent;
	size_t m_count;
};

class REFVariable : public Formula::Variable
{
public:
	REFVariable(Formula::VariableHolder const& parent,size_t N)
		:Variable(parent.GetMax()),m_parent(parent),m_n(N)
	{
	}
	bool CalcNewValue() const
	{
		if (!m_parent.CalcNewValue())
		{
			return false;
		}
		if (m_parent.size() > m_n)
		{
			if (GetVersion() == -1 || GetVersion() < m_parent.GetVersion())
			{
				AddValue(*(m_parent.begin() + m_n));
				SetVersion(m_parent.GetVersion());
			}
			return true;
		}
		return false;
	}
	void Release() const
	{
		m_parent.Release();
	}
private:
	mutable Formula::VariableHolder m_parent;
	size_t m_n;
};

class LNVariable : public Formula::Variable
{
public:
	LNVariable(Formula::VariableHolder const& parent)
		:Variable(parent.GetMax()),m_parent(parent)
	{
	}
	bool CalcNewValue() const
	{
		if (!m_parent.CalcNewValue())
		{
			return false;
		}
		if (m_parent.size() > 0)
		{
			if (GetVersion() == -1 || GetVersion() < m_parent.GetVersion())
			{
				AddValue(log(*(m_parent.begin())));
				SetVersion(m_parent.GetVersion());
			}
			return true;
		}
		return false;
	}
	void Release() const
	{
		m_parent.Release();
	}
private:
	mutable Formula::VariableHolder m_parent;
};


Formula::VariableHolder MA(Formula::VariableHolder const& v,size_t count);
Formula::VariableHolder EMA(Formula::VariableHolder const& v ,size_t count);
Formula::VariableHolder REF(Formula::VariableHolder const& v, size_t N);
Formula::VariableHolder LN(Formula::VariableHolder const& v);


